package com.cyf.common.core.encrypt;

import com.cyf.common.core.codec.HexUtil;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
/**
 * @author chenyifan
 * @create 2024-04-18 15:24
 */
public class MD5Util {

    private static final String HEX_NUMS_STR = "0123456789ABCDEF";
    // 盐的字节长度
    private static final Integer SALT_LENGTH = 12;


    /**
     * 校验验证原始字符串和其加密形式是否匹配
     * @param str
     * @param encryptStr
     * @return
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException
     */
    public static boolean validWithSalt(String str, String encryptStr) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        // 将盐从加密字节数组提取出来
        byte[] encryptByte = hexStringToByte(encryptStr);
        byte[] salt = new byte[SALT_LENGTH];
        System.arraycopy(encryptByte, 0, salt, 0, SALT_LENGTH);
        return encryptStr.equals(encryptedWithSlot(salt, str));
    }

    /**
     * 校验验证原始字符串和其加密形式是否匹配
     * @param str
     * @param encryptStr
     * @return
     * @throws UnsupportedEncodingException
     * @throws NoSuchAlgorithmException
     */
    public static boolean valid(String str, String encryptStr) throws UnsupportedEncodingException, NoSuchAlgorithmException {
        return encryptStr.equals(encrypt(str));
    }

    public static String encrypt(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        // 创建消息摘要对象
        MessageDigest md = MessageDigest.getInstance("MD5");
        // 将口令的数据传给消息摘要对象
        md.update(str.getBytes("UTF-8"));
        // 获得消息摘要的字节数组
        byte[] digest = md.digest();
        // 将加密字节数组转化为16进制字符串
        return HexUtil.bytesTohex(digest);
    }

    /**
     * 使用一个自动生成的盐对输入字符串进行加密
     * @param str   明文字符串
     * @return      加密后的字节数组
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException
     */
    public static String encryptWithGeneratedSalt(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        // 盐值字节数组，用于增加密码哈希的唯一性
        byte[] salt = generateSalt();
        return encryptedWithSlot(salt, str);
    }


    /**
     * 添加盐进行加密
     * @param salt  盐值字节数组，用于增加密码哈希的唯一性
     * @param str   加密字符串
     * @return
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException
     */
    public static String encryptedWithSlot(byte[] salt, String str) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        // 创建消息摘要对象
        MessageDigest md = MessageDigest.getInstance("MD5");
        // 将盐数据传入消息摘要对象
        md.update(salt);
        // 将口令的数据传给消息摘要对象
        md.update(str.getBytes("UTF-8"));
        // 获得消息摘要的字节数组
        byte[] digest = md.digest();

        // 因为要在口令的字节数组中存放盐，所以加上盐的字节长度
        byte[] encryptByte = new byte[digest.length + SALT_LENGTH];
        // 将盐的字节拷贝到加密字节数组的前12个字节，以便在验证口令时取出盐
        System.arraycopy(salt, 0, encryptByte, 0, SALT_LENGTH);
        // 将消息摘要拷贝到加密字节数组从第13个字节开始的字节
        System.arraycopy(digest, 0, encryptByte, SALT_LENGTH, digest.length);
        // 将加密字节数组转化为16进制字符串
        return HexUtil.bytesTohex(encryptByte);
    }

    /**
     * 随机生成盐
     * @return
     */
    private static byte[] generateSalt() {
        SecureRandom random = new SecureRandom();
        byte[] salt = new byte[SALT_LENGTH];
        random.nextBytes(salt);
        return salt;
    }

    /**
     * 将16进制字符转换为字节数组
     * @param hex
     * @return
     */
    private static byte[] hexStringToByte(String hex) {
        int len = (hex.length() / 2);
        byte[] result = new byte[len];
        char[] hexChars = hex.toCharArray();
        for (int i = 0; i < len; i++) {
            int pos = i * 2;
            //1个字节存两个16进制字符，前四位存一个，后四位存一个
            result[i] = (byte) (HEX_NUMS_STR.indexOf(hexChars[pos]) << 4 | HEX_NUMS_STR
                    .indexOf(hexChars[pos + 1]));
        }
        return result;
    }

}
